package us.wili.dev.common.util.qiniu;

import com.google.gson.Gson;
import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.common.ZoneReqInfo;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Map;

/**
 * Created by JasonY on 17/2/20.
 */
public class QiniuUtil {
    private QiniuProperties properties;

    private ZoneReqInfo zoneReqInfo;
    private Auth auth;
    private Configuration configuration;
    private BucketManager bucketManager;
    private UploadManager uploadManager;

    private Gson gson;

    private static final Logger logger = LoggerFactory.getLogger(QiniuUtil.class);

    public QiniuUtil(QiniuProperties properties) {
        this.properties = properties;
        this.configuration = new Configuration(properties.toZone());

        init();
    }

    public QiniuUtil(String accessKey, String secretKey, String bucket, Boolean ifPublic, String domain, String zoneName) {
        QiniuProperties properties = new QiniuProperties(accessKey, secretKey, bucket, ifPublic, domain, zoneName);
        this.configuration = new Configuration(properties.toZone());

        init();
    }

    private void init() {
        this.zoneReqInfo = new ZoneReqInfo(getAccessKey(), getBucket());
        this.auth = Auth.create(getAccessKey(), getSecretKey());
        this.bucketManager = new BucketManager(this.auth, this.configuration);
        this.uploadManager = new UploadManager(this.configuration);
        this.gson = new Gson();
    }

    public String getUpHttp() {
        return getZone().getUpHttp(this.zoneReqInfo);
    }

    public String getUpHttps() {
        return getZone().getUpHttps(this.zoneReqInfo);
    }

    public String getUpBackupHttp() {
        return getZone().getUpBackupHttp(this.zoneReqInfo);
    }

    public String getUpBackupHttps() {
        return getZone().getUpBackupHttps(this.zoneReqInfo);
    }

    /**
     * 简单上传的凭证
     * @return
     */
    public String getEasyUploadToken() {
        return auth.uploadToken(getBucket(), null, getExpireSeconds(), null, true);
    }

    /**
     * 覆盖文件的凭证
     * @return
     */
    public String getCoverUploadToken(String fileKey) {
        return auth.uploadToken(getBucket(), fileKey);
    }

    /**
     * 自定义回复的凭证
     */
    public String getCustomReturnUploadToken(String returnBody) {
        StringMap putPolicy = new StringMap();
        putPolicy.put("returnBody", returnBody);

        return auth.uploadToken(getBucket(), null, getExpireSeconds(), putPolicy);
    }

    public String getCustomReturnUploadToken(Map<String, String> returnMap) {
        return getCustomReturnUploadToken(gson.toJson(returnMap));
    }

    public String getCallbackUploadToken(String callbackUrl, String callbackBody, String callbackBodyType) {
        StringMap putPolicy = new StringMap();
        putPolicy.put("callbackUrl", callbackUrl)
                .put("callbackBody", callbackBody)
                .put("callbackBodyType", callbackBodyType);

        return auth.uploadToken(getBucket(), null, getExpireSeconds(), putPolicy);
    }

    /**
     * 带回调业务服务器的凭证
     * @param callbackBody
     * @param callbackBodyType
     * @return
     */
    public String getCallbackUploadToken(String callbackBody, String callbackBodyType) {
        if (getCallbackUrl() == null || getCallbackUrl().trim().equals("")) {
            throw new QiniuUtilException("callbackUrl can not be null");
        }

        StringMap putPolicy = new StringMap();
        putPolicy.put("callbackUrl", getCallbackUrl())
                .put("callbackBody", callbackBody)
                .put("callbackBodyType", callbackBodyType);

        return auth.uploadToken(getBucket(), null, getExpireSeconds(), putPolicy);
    }

    public String getCallbackUploadToken(String callbackBody) {
        if (getCallbackUrl() == null || getCallbackUrl().trim().equals("")) {
            throw new QiniuUtilException("callbackUrl can not be null");
        }

        if (getCallbackBodyType() == null || getCallbackBodyType().trim().equals("")) {
            throw new QiniuUtilException("callbackBodyType can not be null");
        }

        StringMap putPolicy = new StringMap();
        putPolicy.put("callbackUrl", getCallbackUrl())
                .put("callbackBody", callbackBody)
                .put("callbackBodyType", getCallbackBodyType());

        return auth.uploadToken(getBucket(), null, getExpireSeconds(), putPolicy);
    }

    public boolean isValidCallback(String callbackAuthHeader, String callbackUrl, byte[] callbackBody, String callbackBodyType) {
        return auth.isValidCallback(callbackAuthHeader, callbackUrl, callbackBody, callbackBodyType);
    }

    public boolean isValidCallback(String callbackAuthHeader, byte[] callbackBody, String callbackBodyType) {
        return auth.isValidCallback(callbackAuthHeader, getCallbackUrl(), callbackBody, callbackBodyType);
    }

    public boolean isValidCallback(String callbackAuthHeader, byte[] callbackBody) {
        return auth.isValidCallback(callbackAuthHeader, getCallbackUrl(), callbackBody, getCallbackBodyType());
    }

    public String getDownloadUrl(String fileKey, String attachementName) throws UnsupportedEncodingException {
        return  getIfPublic() ? getPublicDownloadUrl(fileKey, attachementName) : getPrivateDownloadUrl(fileKey, attachementName);
    }

    public String getDownloadUrl(String fileKey) throws UnsupportedEncodingException {
        return  getIfPublic() ? getPublicDownloadUrl(fileKey) : getPrivateDownloadUrl(fileKey);
    }

    private String getPublicDownloadUrl(String fileKey, String attachmentName) throws UnsupportedEncodingException {
        if (fileKey == null) {
            return null;
        }

        String encodedFileKey = URLEncoder.encode(fileKey, "UTF-8");
        String publicUrl = String.format("%s/%s", getDomain(), encodedFileKey);

        if (attachmentName != null && !attachmentName.trim().equals("")) {
            publicUrl = publicUrl + "?attname=" + URLEncoder.encode(attachmentName, "UTF-8");
        }

        return publicUrl;
    }

    private String getPublicDownloadUrl(String fileKey) throws UnsupportedEncodingException {
        return getPublicDownloadUrl(fileKey, null);
    }

    private String getPrivateDownloadUrl(String fileKey, String attachmentName) throws UnsupportedEncodingException {
        if (fileKey == null) {
            return null;
        }

        String publicUrl = getPublicDownloadUrl(fileKey, attachmentName);

        return auth.privateDownloadUrl(publicUrl, getExpireSeconds());
    }

    private String getPrivateDownloadUrl(String fileKey) throws UnsupportedEncodingException {
        return getPrivateDownloadUrl(fileKey, null);
    }

    public void move(String fromKey, String toBucket, String toKey) throws QiniuException {
        bucketManager.move(getBucket(), fromKey, toBucket, toKey);
    }

    public void copy(String fromKey, String toBucket, String toKey) throws QiniuException {
        try {
            bucketManager.copy(getBucket(), fromKey, toBucket, toKey);
        } catch (QiniuException e) {
            logger.info(e.getMessage());
        }
    }

    public DefaultPutRet upload(String fileName, String key, String token) throws QiniuException {
        Response response = this.uploadManager.put(fileName, key, getEasyUploadToken());

        DefaultPutRet ret = this.gson.fromJson(response.bodyString(), DefaultPutRet.class);

        return ret;
    }

    public void download(String urlString, String filename) throws IOException {
        // 构造URL
        URL url = new URL(urlString);
        // 打开连接
        URLConnection con = url.openConnection();
        // 输入流
        InputStream is = con.getInputStream();
        // 1K的数据缓冲
        byte[] bs = new byte[1024];
        // 读取到的数据长度
        int len;
        // 输出的文件流
        OutputStream os = new FileOutputStream(filename);
        // 开始读取
        while ((len = is.read(bs)) != -1) {
            os.write(bs, 0, len);
        }
        // 完毕，关闭所有链接
        os.close();
        is.close();
    }

    public long getExpireSeconds() {
        return properties.getExpireSeconds();
    }

    public void setExpireSeconds(long expireSeconds) {
        this.properties.setExpireSeconds(expireSeconds);
    }

    public Configuration getConfiguration() {
        return configuration;
    }

    public void setConfiguration(Configuration configuration) {
        this.configuration = configuration;
    }

    public BucketManager getBucketManager() {
        return bucketManager;
    }

    public void setBucketManager(BucketManager bucketManager) {
        this.bucketManager = bucketManager;
    }

    public String getAccessKey() {
        return properties.getAccessKey();
    }

    public String getSecretKey() {
        return properties.getSecretKey();
    }

    public String getBucket() {
        return properties.getBucket();
    }

    public String getDomain() {
        return properties.getDomain();
    }

    public Zone getZone() {
        return properties.toZone();
    }

    public Auth getAuth() {
        return auth;
    }

    public Gson getGson() {
        return gson;
    }

    public Boolean getIfPublic() {
        return properties.getIfPublic();
    }

    public UploadManager getUploadManager() {
        return uploadManager;
    }

    public String getCallbackUrl() {
        return properties.getCallbackUrl();
    }

    public void setCallbackUrl(String callbackUrl) {
        properties.setCallbackUrl(callbackUrl);
    }

    public void setCallbackBodyType(String callbackBodyType) {
        properties.setCallbackBodyType(callbackBodyType);
    }

    public String getCallbackBodyType() {
        return properties.getCallbackBodyType();
    }
}
